package com.siteseat.common.spring;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.core.io.Resource;

import com.siteseat.common.exception.InternalException;

/**
 *
 */
public class ModularContext extends AbstractXmlApplicationContext implements ApplicationContextAware {
    
  private ExposedBeanSet exposedByModules = new ExposedBeanSet();
  
  private List<String> exposing = new ArrayList<String>();
  
  private Set<String>  configurationFiles = new HashSet<String>();
  
  private Resource[] configrationResources;
           
  private List<String> startUpBeans;
  
  private Map<String,ModularContext> modules = new HashMap<String,ModularContext>();
  
  private ModularContext owner;
  
  /**
   * Gets the exposing
   *
   * @return the exposing
   */
  public List<String> getExposing() {
    return exposing;
  }

  /**
   * Sets the exposing
   *
   * @param exposing the exposing to set
   */
  public void setExposing( List<String> exposing ) {
    this.exposing = exposing;
  }

  /**
   * Gets the startUpBeans
   *
   * @return the startUpBeans
   */
  public List<String> getStartUpBeans() {
    return startUpBeans;
  }

  /**
   * Sets the startUpBeans
   *
   * @param startUpBeans the startUpBeans to set
   */
  public void setStartUpBeans( List<String> startUpBeans ) {
    this.startUpBeans = startUpBeans;
  }

  public void addStartupBean( String startUpBean ) {
    if( this.startUpBeans == null ) {
      this.startUpBeans = new ArrayList<String>();
    }
    this.startUpBeans.add( startUpBean );
  }

  /**
   * Gets the configurationFiles
   
   * @return the configurationFiles
   */
  public Set<String> getConfigurationFiles() {
    return configurationFiles;
  }

  /**
   * Sets the configurationFiles
   *
   * @param configurationFiles the configurationFiles to set
   */
  public void setConfigurationFiles( Set<String> configurationFiles ) {
    this.configurationFiles.clear();
    this.configurationFiles.addAll( configurationFiles );
  }
  
  /**
   * Sets the configurationFiles
   *
   * @param files the files to set
   */
  public void addConfigurationFile( String... files ) {
    this.configurationFiles.addAll( Arrays.asList( files ) );
  }

  /**
   * 
   *
   * @param module
   * @param beans
   */
  public void addModule( ModularContext module ) {
    String moduleId = module.getId();
    if( modules.containsKey( moduleId ) ) {
      throw new InternalException( "Duplicated sub module: " + moduleId );
    }
    
    exposedByModules.add( module, ExposedBean.parse( module.getExposing() ) );
    
    module.setParent( this );
    modules.put( moduleId, module );
  }

  /** {@inheritDoc} */ 
  @Override
  protected Resource[] getConfigResources() {
    return configrationResources;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void refresh() throws BeansException, IllegalStateException {
    if( this.owner != null ) {
      this.owner.addModule( this );
    }

    Set<String> cfs = getConfigurationFiles();
    final Resource[] rs = new Resource[cfs.size()];
    int i = 0;
    for( String r: cfs ) {
      rs[i] = getResource( r );
      logger.info( "Context resource: " + rs[i] );
      i++;
    }
    configrationResources = rs;
    
    super.refresh();
    
    List<String> suBeanNames = getStartUpBeans();
    if( suBeanNames != null && suBeanNames.size() > 0 ) {
      logger.info( "Initializing module beans." );
      for( String n: suBeanNames ) {
        logger.info( "Startup bean: <" + n + ">" );
        getBean( n );
      }
      logger.info( "Initialized module beans." );
    }
    
    if( exposedByModules.isNotEmpty() ) {
      logger.info( "Retrieving exposed beans." );
      exposedByModules.initializeAll();
      logger.info( "Retrieved exposed beans." );
    }
    
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void setApplicationContext( ApplicationContext applicationContext ) throws BeansException {
    if( applicationContext instanceof ModularContext ) {
      this.owner = (ModularContext)applicationContext;
    } else {
      this.owner = null;
    }
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  protected DefaultListableBeanFactory createBeanFactory() {
    return new ModularBeanFactory(exposedByModules, getInternalParentBeanFactory());
  }
  
}
